﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace udp2rs_con
{
    class Program
    {
        private static Timer timer;
        private static UdpClient  udpcRecv;
        private static IPEndPoint ipepTarget;
        private static IPEndPoint ipepTarget2;
        private static SerialPort ComDevice;
        private static UdpClient udpcSend;
        private static long lUdp2Rs;//UDP到RS的字节数
        private static long lRS2UDP;//串口到UDP的字节数
        private static Thread thrRecv;//UDP->RS232转发线程
        /// <summary>
        /// 传感器数据
        /// </summary>
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]//对齐方式
        public struct SensorData
        {
            public byte SynCode1;//同步码1 0xEB
            public byte SynCode2;//同步码2 0x90
            public byte Length;//长度 0x36
            public short OmegaX;//滚转角速度 分辨率0.1度/秒 范围-300~300
            public short OmegaZ;//俯仰角速度 分辨率0.1度/秒 范围-300~300
            public short OmegaY;//偏航角速度 分辨率0.1度/秒 范围-300~300
            public short Roll;//滚转角 分辨率0.1度 范围-60~60
            public short Pitch;//俯仰角 分辨率0.1度 范围-60~60
            public ushort Yaw;//航向角 分辨率0.1度 范围0~360
            public short Accex;//侧向加速度 分辨率0.01m/s2，范围-100~100
            public short Accey;//轴向加速度 分辨率0.01m/s2，范围-100~100
            public short Accez;//法向加速度 分辨率0.01m/s2，范围-100~100
            public Int32 Lon;//经度，分辨率10^-6(角度)，范围-180~180
            public Int32 Lat;//纬度，分辨率10^-6(角度)，范围-90~90
            public Int32 AltAboveSea;//海拔高度，分辨率0.25m，范围-500~12000
            public Int32 AltAtom;//气压高度，分辨率0.25m，范围-500~12000
            public UInt32 AltAboveGround;//离地高度，分辨率0.25m，范围0~12000
            public ushort AirSpeed;//空速 0.01m/s 0~360
            public short Alpha;//迎角 0.1º -60~60
            public short Beta;//侧滑角 0.1º -60~60
            public short Velo_N;//北向速度 0.01m/s 0~360
            public short Velo_E;//东向速度 0.01m/s 0~360
            public short Velo_G;//地向速度 0.01m/s 0~360
            public byte CheckCode;//校验码
        }

        /// <summary>
        /// 串口数据位列表（5,6,7,8）
        /// </summary>
        public enum SerialPortDatabits : int
        {
            FiveBits = 5,
            SixBits = 6,
            SeventBits = 7,
            EightBits = 8
        }

        /// <summary>
        /// 串口波特率列表。
        /// 75,110,150,300,600,1200,2400,4800,9600,14400,19200,28800,38400,56000,57600,
        /// 115200,128000,230400,256000
        /// </summary>
        public enum SerialPortBaudRates : int
        {
            BaudRate_75 = 75,
            BaudRate_110 = 110,
            BaudRate_150 = 150,
            BaudRate_300 = 300,
            BaudRate_600 = 600,
            BaudRate_1200 = 1200,
            BaudRate_2400 = 2400,
            BaudRate_4800 = 4800,
            BaudRate_9600 = 9600,
            BaudRate_14400 = 14400,
            BaudRate_19200 = 19200,
            BaudRate_28800 = 28800,
            BaudRate_38400 = 38400,
            BaudRate_56000 = 56000,
            BaudRate_57600 = 57600,
            BaudRate_115200 = 115200,
            BaudRate_128000 = 128000,
            BaudRate_230400 = 230400,
            BaudRate_256000 = 256000
        }

        static private void ReceiveMessageAndResend(object obj)
        {
            IPEndPoint remoteIpep = new IPEndPoint(IPAddress.Any, 0);

            while (true)
            {
                try//先检查udp口是否接收到数据，并转发
                {
                    byte[] bytRecv = udpcRecv.Receive(ref remoteIpep);
                    SendDataSerialPort(bytRecv);
                    lUdp2Rs += bytRecv.Length;
                    //Console.Write("\r UDP->RS232 Bytes:{0},R232->UDP Bytes:{1}", lUdp2Rs, lRS2UDP);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("UDP接收错误,错误信息如下："+ex.Message);
                    break;
                }
            }
        }

        private static bool SendDataSerialPort(byte[] data)
        {
            if (ComDevice.IsOpen)
            {
                try
                {
                    ComDevice.Write(data, 0, data.Length);//发送数据
                    return true;
                }
                catch (Exception ex)
                {
                    Console.WriteLine("串口打开错误:"+ex.Message);
                }
            }
            else
            {
                Console.WriteLine("串口未打开");
            }
            return false;
        }


        private static void Com_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
                byte[] ReDatas = new byte[ComDevice.BytesToRead];
                try
                {
                    ComDevice.Read(ReDatas, 0, ReDatas.Length);
                    
                    SendDataUDPPort(ReDatas);
                    lRS2UDP += ReDatas.Length;
                }
                catch (Exception ex)
                {
                    Console.WriteLine("串口接收错误:"+ ex.Message);
                    return;
                }
        }

        private static void SendDataUDPPort(byte[] ReDatas)
        {
            if (null == ReDatas)
                return;
            if(null == udpcSend)
            {
                udpcSend = new UdpClient();
            }
            try
            {
                udpcSend.Connect(ipepTarget);
                udpcSend.Send(ReDatas, ReDatas.Length);

                udpcSend.Connect(ipepTarget2);
                udpcSend.Send(ReDatas, ReDatas.Length);
            }
            catch (Exception ex)
            {
                Console.WriteLine("UDP发送错误:"+ ex.Message);
                return;
            }
        }

        static void refreshByteCount(object obj)
        {
            Console.Write("\rUDP->RS232 Bytes:{0},R232->UDP Bytes:{1}", lUdp2Rs, lRS2UDP);
        }

        ~Program()//析构程序，关闭控制台窗体时使用
        {
            //关闭串口
            if(ComDevice.IsOpen)
            {
                ComDevice.Close();
                ComDevice.Dispose();  
            }

            //关闭转发线程
            if(thrRecv.IsAlive)
            {
                thrRecv.Abort();
            }
        }
        static void Main(string[] args)
        {

            //byte[] data;
            IPEndPoint ipepLocal;




            Console.WriteLine("读取通信配置...");
            //将XML文件加载进来

            XDocument document;
            try
            {
                document = XDocument.Load("conf.xml");
            }
            catch (Exception ex)
            {
                Console.WriteLine("加载配置文件错误，请检查是否存在，错误信息如下：" + ex.Message);
                return;
            }


            //获取到XML的根元素进行操作
            XElement root = document.Root;
            try
            {//设置串口
                ComDevice = new SerialPort();
                Console.WriteLine("读取并设置串口信息");
                XElement eleCom = root.Element("串口设置");
                XElement eleComPort = eleCom.Element("端口");

                ComDevice.PortName = eleComPort.Value;
                Console.WriteLine("串口号:" + ComDevice.PortName);
                ComDevice.BaudRate = Convert.ToInt32(eleCom.Element("波特率").Value);
                Console.WriteLine("波特率:" + ComDevice.BaudRate);

                string strParity = eleCom.Element("校验位").Value;
                switch(strParity)
                {
                    case "None":
                        ComDevice.Parity = Parity.None;
                        break;
                    case "Even":
                        ComDevice.Parity = Parity.Even;
                        break;
                    case "Odd":
                        ComDevice.Parity = Parity.Odd;
                        break;
                    case "Mark":
                        ComDevice.Parity = Parity.Mark;
                        break;
                    case "":
                        ComDevice.Parity = Parity.Space;
                        break;
                    default:
                        ComDevice.Parity = Parity.None;
                        break;
                }
                Console.WriteLine("校验位:" + ComDevice.Parity);

                ComDevice.DataBits = Convert.ToInt32(eleCom.Element("数据位").Value);
                Console.WriteLine("数据位:" + ComDevice.DataBits);

                string strStopBit = eleCom.Element("停止位").Value;
                switch (strStopBit)
                {
                    case "None":
                        ComDevice.StopBits = StopBits.None;
                        break;
                    case "One":
                        ComDevice.StopBits = StopBits.One;
                        break;
                    case "OnePointFive":
                        ComDevice.StopBits = StopBits.OnePointFive;
                        break;
                    case "Two":
                        ComDevice.StopBits = StopBits.Two;
                        break;
                    default:
                        ComDevice.StopBits = StopBits.None;
                        break;
                }
                Console.WriteLine("停止位:" + ComDevice.StopBits);
            }
            catch (Exception ex)
            {
                Console.WriteLine("串口初始化错误,错误信息如下："+ex.Message);
                return;
            }


           


            //初始化UDP
            XElement eleUdp = root.Element("UDP设置");
            XElement eleUdpLocal = eleUdp.Element("本地UDP设置");
            string strIPLocal = eleUdpLocal.Element("IP").Value;
            
            IPAddress ipLocal = IPAddress.Parse(strIPLocal);
            Console.WriteLine("本地IP:" + strIPLocal);
            string strPortLocal = eleUdpLocal.Element("端口").Value;
            Console.WriteLine("本地端口:" + strPortLocal);
            int iPortLocal = Convert.ToInt32(strPortLocal);
            try
            {
                ipepLocal = new IPEndPoint(ipLocal, iPortLocal);
                udpcRecv = new UdpClient(ipepLocal);
            }catch(Exception ex)
            {
                Console.WriteLine("本地UDP初始化错误，错误信息如下：" + ex.Message);
                return;
            }

            XElement eleUdpTarget1 = eleUdp.Element("目标UDP1");
            string strIPTgt1 = eleUdpTarget1.Element("IP").Value;

            IPAddress ipTgt1 = IPAddress.Parse(strIPTgt1);
            Console.WriteLine("目标IP1:" + ipTgt1);
            string strPortTgt1 = eleUdpTarget1.Element("端口").Value;
            Console.WriteLine("目标端口1:" + strPortTgt1);
            int iPortTgt1 = Convert.ToInt32(strPortTgt1);
            ipepTarget = new IPEndPoint(ipTgt1,iPortTgt1);

            XElement eleUdpTarget2 = eleUdp.Element("目标UDP2");
            string strIPTgt2 = eleUdpTarget2.Element("IP").Value;

            IPAddress ipTgt2 = IPAddress.Parse(strIPTgt2);
            Console.WriteLine("目标IP2:" + ipTgt2);
            string strPortTgt2= eleUdpTarget2.Element("端口").Value;
            Console.WriteLine("目标端口2:" + strPortTgt2);
            int iPortTgt2= Convert.ToInt32(strPortTgt2);
            ipepTarget2 = new IPEndPoint(ipTgt2, iPortTgt2);
            //初始化目标UDP连接点
            lRS2UDP = 0;
            ComDevice.DataReceived += new SerialDataReceivedEventHandler(Com_DataReceived);//绑定事件
            try
            {
                ComDevice.Open();
                Console.WriteLine("串口" + ComDevice.PortName + "已打开");
            }
            catch (Exception ex)
            {
                Console.WriteLine("串口打开错误，错误信息如下：" + ex.Message);
                return;
            }


            thrRecv = new Thread(ReceiveMessageAndResend);//监听并转发的线程
            lUdp2Rs = 0;
            thrRecv.Start();
            Console.WriteLine("UDP->RS232 监听转发已开启");

            timer = new Timer(refreshByteCount,0,0,500);//0.5秒刷新一次转发字节数

            Console.ReadKey();

        }
    }
}
